/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_RENDERSYSTEM_H
#define M_RENDERSYSTEM_H

#include "factory.h"
#include "presentparams.h"
#include "types.h"
#include "refptr.h"
#include "color.h"
#include "common.h"
#include "matrix4.h"
#include "signal.h"
#include "hardwarebuffer.h"
#include "vertexdeclaration.h"
#include "renderdata.h"
#include "blendmode.h"

#include <map>
#include <vector>


namespace Engine
{
    /** Defines the frame buffer types. */
    enum FrameBufferType
	{
        FBT_COLOUR  = 0x1,
        FBT_DEPTH   = 0x2,
        FBT_STENCIL = 0x4
    };

	/// Enum describing the ways to generate texture coordinates
    enum TexCoordCalcMethod
    {
        /// No calculated texture coordinates
        TEXCALC_NONE,
        /// Environment map based on vertex normals
        TEXCALC_ENVIRONMENT_MAP,
        /// Environment map based on vertex positions
        TEXCALC_ENVIRONMENT_MAP_PLANAR,
        TEXCALC_ENVIRONMENT_MAP_REFLECTION,
        TEXCALC_ENVIRONMENT_MAP_NORMAL,
        /// Projective texture
        TEXCALC_PROJECTIVE_TEXTURE
    };
    /// Enum describing the various actions which can be taken onthe stencil buffer
    enum StencilOperation
    {
        /// Leave the stencil buffer unchanged
        SOP_KEEP,
        /// Set the stencil value to zero
        SOP_ZERO,
        /// Set the stencil value to the reference value
        SOP_REPLACE,
        /// Increase the stencil value by 1, clamping at the maximum value
        SOP_INCREMENT,
        /// Decrease the stencil value by 1, clamping at 0
        SOP_DECREMENT,
        /// Increase the stencil value by 1, wrapping back to 0 when incrementing the maximum value
        SOP_INCREMENT_WRAP,
        /// Decrease the stencil value by 1, wrapping when decrementing 0
        SOP_DECREMENT_WRAP,
        /// Invert the bits of the stencil buffer
        SOP_INVERT
    };

	/// Enum describing the various texture coordinate dimension
	enum TexCoordDim
	{
		TCD_DISABLE,
		TCD_COUNT1,
		TCD_COUNT2,
		TCD_COUNT3,
		TCD_COUNT4,
		TCD_PROJECTED
	};

	/// Render primitive type for render operation
	enum PrimitiveType
	{
		PT_TRIANGLELIST,
		PT_POINTLIST
	};

	class Light;
	/**
	*   Abstract class which implement public interface to render systems.
	*/
	class RenderSystem
	{
		friend class Graphic;
		typedef std::map< std::string, RefPtr<RenderTarget,WeakPtrExtension> > RenderTargetMap;
		typedef std::multimap<  unsigned int, RefPtr<RenderTarget,WeakPtrExtension> > RenderTargetPriorityMap;

	public:
		typedef std::vector<LightPtr> LightVector;

		/**
		*   Constructor.
		*/
		RenderSystem();

		/**
		*   Destructor.
		*/
		virtual ~RenderSystem();

		/**
		*   Delete resources. Allow derived classes to free critical resources before systems objects.
		*/
		virtual void ShutDown();

		/**
		*   Create new render window.
		*/
		virtual RenderWindowPtr CreateRenderWindow(const std::string& name, const PresentParams& pp) = 0;

        /**
		*   Clears one or more frame buffers on the active render target.
        *   @param buffers Combination of one or more elements of FrameBufferType denoting which buffers are to be cleared
        *   @param color The color to clear the color buffer with, if enabled
        *   @param depth The value to initialize the depth buffer with, if enabled
        *   @param stencil The value to initialize the stencil buffer with, if enabled.
        */
        virtual void ClearFrameBuffer(unsigned int buffers,
            const Color& colour = Color::Black,
            REAL depth = 1.0, unsigned short stencil = 0) = 0;

		/**
		*   Set current render target for render to.
		*/
		virtual void SetRenderTarget(RenderTarget* renderTarget) = 0;

		/**
		*   Start calculating triangles.
		*/
		void BeginFaceCount(){m_facesCount = 0;}

		/**
		*   Return number of rendered faces.
		*/
		unsigned int GetFaceCount(){return m_facesCount;}

		/**
		*   Begin scene rendering.
		*/
		virtual void BeginFrame() = 0;

		/**
		*   Finish scene rendering.
		*/
		virtual void EndFrame() = 0;

		/**
		*   Sets how to rasterise triangles, as points, wireframe or solid polys.
		*/
		virtual void SetPolygonMode(PolygonMode level) = 0;

		/**
		*   Sets the view transform matrix.
		*/
        virtual void SetViewMatrix(const Matrix4<REAL> &m) = 0;

        /**
		*   Sets the projection transform matrix.
		*/
        virtual void SetProjectionMatrix(const Matrix4<REAL> &m) = 0;

        /**
		*   Sets the world transform matrix.
		*/
        virtual void SetWorldMatrix(const Matrix4<REAL> &m) = 0;

		/**
		*   Set scene ambient light.
		*/
		virtual void SetAmbientLight(const Color& color) = 0;

		/**
		*   Setup lights for scene.
		*/
		virtual void UseLights(const LightVector& lights) = 0;

		/**
		*   Render geometries.
		*/
		virtual void Render(RenderDataPtr  renderData);

		/**
		*   Fired before system reset.
		*/
		Signal<> StartResetSignal;

		/**
		*   Fired after system reset.
		*/
		Signal<> EndResetSignal;

		/**
		*   Create index buffer.
		*/
		virtual RefPtr<HardWareBuffer, WeakPtrExtension> CreateIndexBuffer(unsigned int facecount, IndexType type) = 0;

		/**
		*   Create vertex buffer.
		*/
		virtual RefPtr<HardWareBuffer, WeakPtrExtension> CreateVertexBuffer(unsigned int vertexCount, unsigned int vertexSize) = 0;

		/**
		*   Create vertex declaration.
		*/
		virtual RefPtr<VertexDeclaration, WeakPtrExtension> CreateVertexDeclaration() = 0;

		/**
		*   Create render data for rendering API.
		*/
		virtual RefPtr<RenderData, WeakPtrExtension> CreateRenderData(unsigned int faceCount, unsigned int vertexCount, VertexDeclarationPtr vertexDeclaration, HardWareBufferPtr indexData, HardWareBufferPtr vertexData, PrimitiveType primitiveType = PT_TRIANGLELIST) = 0;


		//-----------------------------------Render specific methods-----------------------------------------
		virtual void SetDepthEnable(bool val) = 0;
		virtual void SetDepthWriteEnable(bool val) = 0;
		virtual void SetMaterial(const Color& ambientColor = Color::White, const Color& diffuseColor = Color::White, const Color& emissiveColor = Color::White, const Color& specularColor = Color::White, REAL power = 0) = 0;
		virtual void SetShadeMode(ShadeMode val) = 0;
		virtual void SetCullMode(FaceCullMode val) = 0;
		virtual void SetLightingEnable(bool val) = 0;
		virtual void SetPointSpritesEnabled(bool enabled) = 0;
		virtual void SetPointParameters(REAL size, bool attenuationEnabled,REAL constant, REAL linear, REAL quadratic, REAL minSize, REAL maxSize) = 0;
		virtual void SetTexture(size_t unit, TexturePtr texPtr) = 0;
		virtual void SetTextureCoordSet(size_t unit, TexCoordCalcMethod m, size_t index) = 0;
		virtual void SetTextureBlendMode(size_t unit, const LayerBlendModeEx& bm) = 0;
		virtual void SetTextureUnitFiltering(size_t unit, FilterType ftype, FilterOptions filter) = 0;
		virtual unsigned int GetCurrentAnisotropy(size_t unit) = 0;
		virtual void SetTextureLayerAnisotropy(size_t unit, unsigned int maxAnisotropy) = 0;
		virtual void SetTextureAddressingMode(size_t unit, const UVWAddressingMode& uvw) = 0;
		virtual void SetTextureBordercolor(size_t unit, const Color& color) = 0;
		virtual void SetTextureMipmapBias(size_t unit, REAL bias) = 0;
		virtual void SetTextureMatrix(size_t unit, const Matrix4<REAL>& xform, TexCoordDim texCoordDim) = 0;
		virtual void SetSceneBlending(SceneBlendFactor sourceFactor, SceneBlendFactor destFactor) = 0;
		virtual void SetAlphaRejectSettings(CompareFunction func, unsigned char value) = 0;
		virtual void SetDepthBufferFunction(CompareFunction func = CMPF_LESS_EQUAL) = 0;
		virtual void SetColorBufferWriteEnabled(bool red, bool green, bool blue, bool alpha) = 0;
		virtual void SetDepthBias(REAL constantBias, REAL slopeScaleBias = 0.0f) = 0;
		virtual void SetFog(FogMode mode = FOG_NONE, const Color& color = Color::White, REAL expDensity = 1.0, REAL linearStart = 0.0, REAL linearEnd = 1.0) = 0;
		virtual void SetStencilCheckEnabled(bool enabled) = 0;
		virtual void SetStencilBufferParams(CompareFunction func = CMPF_ALWAYS_PASS,unsigned int refValue = 0, unsigned int mask = 0xFFFFFFFF,StencilOperation stencilFailOp = SOP_KEEP,StencilOperation depthFailOp = SOP_KEEP,StencilOperation passOp = SOP_KEEP,bool twoSidedOperation = false) = 0;
		virtual void SetNormaliseNormals(bool normalise) = 0;
		virtual void SetClipPlane (unsigned short index, REAL A, REAL B, REAL C, REAL D) = 0;
		virtual void SetEenableClipPlane (unsigned short index, bool enable) = 0;
		virtual void SetScissorTest(bool enabled, size_t left = 0, size_t top = 0,size_t right = 800, size_t bottom = 600) = 0;

//virtual void bindGpuProgram(GpuProgram* prg);
//virtual void bindGpuProgramParameters(GpuProgramType gptype, GpuProgramParametersSharedPtr params) = 0;
//virtual void bindGpuProgramPassIterationParameters(GpuProgramType gptype) = 0;
//virtual void unbindGpuProgram(GpuProgramType gptype);

		//---------------------------------------------------------------------------------------------------

	protected:

		/**
		*   Attach render target to system.
		*/
		void AttachRenderTarget(RefPtr<RenderTarget,WeakPtrExtension> renderTarget);

	private:

		/**
		*   Render frame in all render targets.
		*/
		void UpdateAllRenderTargets();

	private:

		RenderTargetMap m_renderTargets;

		RenderTargetPriorityMap m_prioritisedRenderTargets;

		unsigned int m_facesCount;
	};

	/**
	*   Base class for all factories for render systems.
	*/
	class RenderSystemFactory: public Factory<RenderSystem>
	{

	};
}

#endif

